#include "file_name_system.h"
#include "define.h"
#include "../utils/util.h"
#include <string>
#include <vector>
using namespace std;

namespace PFS
{
    FileNameSystem::FileNameSystem()
        : file_count(0), dir_count(0)
    {
        root = new INodeDict();
    }
    virtual ~FileNameSystem()
    {
        if(NULL != root)
            delete root;
    }

    void FileNameSystem::add_node(INode* node, INode* parent)
    {
        vector<int> vec;
        return_if_fail(parent->is_directory());
        INodeDict* dict = reinterpret_cast<INodeDict*>(parent);
        dict->add_child(node);
        if(!node->is_directory())
        {
            file_list.add_file(node->get_name(), reinterpret_cast<INodeFile*>(node));
            file_count++;
        }
        else
            dir_count++;
    }

    void FileNameSystem::add_node(const string& path, INode* node)
    {
        INode* tmp = find_node_by_path(path);
        return_if_fail( tmp && tmp->is_directory());
        reinterpret_cast<INodeDict*>(tmp)->add_child(node);
        cout<<"add node success"<<endl;
        if(!node->is_directory())
        {
            file_list.add_file(node->get_name(), reinterpret_cast<INodeFile*>(node));
            file_count++;
        }
        else
            dir_count++;
    }

    INode* FileNameSystem::find_node_by_path(const string& path)
    {
        INode* tmp;
        tmp = file_list.find_file(path.c_str());//check if is a file
        return_val_if_fail(tmp == NULL, tmp); //find ,just return 
        vector<string> strs = split(path, DELEMIT);
        int len = strs.size();
        string tmp_str = DELEMIT;
        tmp_str.append(strs[0]);
        tmp = reinterpret_cast<INodeDict*>(root)->get_child(strs[0]);
        return_val_if_fail(((tmp != NULL) && tmp->is_directory()), NULL);
        for(int i = ; i < strs.size(); i++)
        {
            tmp_str.append(DELEMIT);
            tmp_str.append(strs[i]);
            tmp = reinterpret_cast<INodeDict*>(tmp)->get_child(strs[i]);
            return_val_if_fail(tmp != NULL, NULL);
            if( i != (strs.size() -1) && (!tmp->is_directory()))
            {
                cout<<tmp_str<<" is a file, how can it have children?"<<endl;
                return NULL;
            }
        }
        return tmp;
    }

    INode* FileNameSystem::find_node_by_name(const string& name, const INode* parent)
    {//return file or dict
        return_val_if_fail(parent->is_directory(), NULL);
        return reinterpret_cast<INodeDict*>(parent)->get_child(name);
    }

    INode* FileNameSystem::find_file(const string& file_name, const INode* parent)
    {//must return file. else return null,file_name is a relative path
        string name;
        return_val_if_fail(parent->is_directory(), NULL);
        string::size_type loc = file_name.find(DELEMIT, 0);
        if(loc != string::npos)
        {
            vector<string> names = split(file_name, DELEMIT);
            name = names[names.size() -1];
        }
        else
            name = file_name;
        INode* tmp = reinterpret_cast<INodeDict*>(parent)->get_child(name);
        return_val_if_fail(tmp != NULL && (!tmp->is_directory()), NULL);
        return tmp;
    }

    INode* FileNameSystem::find_file(const string& file_name)
    {//file_name is a abs-path
        return file_list.find_file(file_name.c_str());
    }

    void FileNameSystem::add_block(const uint32_t block_id, BlockLocate* block)
    {
        return_if_fail(block_id == block->get_block_info()->block_id);
        string file_name = block->get_block_info()->file_name;
        INodeFile* tmp = file_list.find_file(file_name.c_str());
        if(NULL == tmp)
        {
            string pwd = get_pwd(file_name);
            INode* node = find_node_by_path(pwd);
            return_if_fail(node != NULL);
            tmp = new INodeFile(file_name);
            add_node(tmp, node);
        }
        tmp->add_block(block);
        block_file_list.add_map_file(block_id, file_name);
    }

    void FileNameSystem::add_block(BlockLocate* block, INode* node)
    {
        return_if_fail(!node->is_directory());
        reinterpret_cast<INodeFile*>(node)->add_block(block);
    }

    bool exsit_block(const uint32_t block_id)
    {
        return block_file_list.exsit(block_id);
    }
    
    BlockLocate* FileNameSystem::create_block(const string &file_name, const uint32_t block_id)
    {//create a blocklocate,and we should fill its blick_info,if succ, add it to the file system
        INode* tmp = find_node_by_path(file_name);
        if(NULL == tmp)
        {
            //no such file,if its dic exsit, create one
            string pwd = get_pwd(file_name);
            INode* parent = find_node_by_path(pwd);
            return_val_if_fail(parent != NULL, NULL);
            tmp = new INodeFile(file_name);
            add_node(tmp, parent);
        }
        BlockLocate* block = reinterpret_cast<INodeFile*>(tmp)->create_block(block_id);
        block->get_block_info()->file_name = file_name;
        add_block(block, tmp);
        return block;
    }

    void FileNameSystem::remove_node_by_path(const string& path, bool force_del)
    {
        INode* node = find_node_by_path(path);
        return_val_if_fail(node != NULL);
        node->remove_node(force_del);
    }

    void FileNameSystem::remove_node_by_name(const string &name, INode* parent, bool force_del)
    {
        return_if_fail(parent != NULL && parent->is_directory());
        reinterpret_cast<INodeDict*>(parent)->remove_child(name, force_del);
    }

    INode* FileNameSystem::find_file_by_block(const uint32_t block_id)
    {
        string file_name = block_file_list.find_map_file(block_id);
        return_val_if_fail(!file_name.empty(), NULL);
        return find_file(file_name);
    }

    BlockLocate* FileNameSystem::find_block(const uint32_t block_id)
    {
        INode *node = find_file_by_block(block_id);
        return_val_if_fail(node != NULL, NULL);
        return reinterpret_cast<INodeFile*>(node)->find_block(block_id);
    }

    void FileNameSystem::remove_block(const uint32_t block_id)
    {
        INode *node = find_file_by_block(block_id);
        return_val_if_fail(node != NULL, NULL);
        reinterpret_cast<INodeFile *>(node)->remove_block(block_id);
    }

    void FileNameSystem::remove_block(const uint32_t block_id, INode* node)
    {
        return_if_fail((node != NULL) && (!node->is_directory()));
        reinterpret_cast<INodeFile *>(node)->remove_block(block_id);
    }

    BlockLocate* FileNameSystem::replace_block(const uint32_t block_id, BlockLocate* new_blk)
    {
        INode *node = find_file_by_block(block_id);
        return_val_if_fail((node != NULL) && (!node->is_directory()), NULL);
        return reinterpret_cast<INodeFile *>(node)->replace_block(block_id, new_blk);
    }
}
